﻿using AuSys.Database.Context;
using AuSys.Models;

namespace AuSys.Repos
{
    /// <summary>
    /// 带有缓存机制的数据库交互类<br/>
    /// 适用于“读多写少”的表，TODO：“读多写多”的缓存另外写一个<br/>
    /// 缓存将会取走**全部行**指定的列，并存为静态List<br/>
    /// 可使用CacheSearch方法筛选并取走缓存元素<br/>
    /// 特性：每个Scope仅检查一次缓存（除非本Scope内进行了数据库操作）
    /// </summary>
    /// <typeparam name="T">实体类</typeparam>
    /// <typeparam name="TCache">缓存类</typeparam>
    /// <param name="context">数据库上下文</param>
    public abstract class RepoCache<T, TCache>(
        DataContext context
        ) : Repo<T>(context) where T: class, IDbModel
    {
        protected override bool EnableLastUpdateRecord => true;
        private readonly static List<TCache> Cache = [];
        private readonly static Lock CacheAccessLock = new();
        private static DateTime CacheLastUpdate = DateTime.MinValue;
        /// <summary>
        /// 是否无法确定缓存过期情况
        /// </summary>
        private static bool CacheTrustable = false;
        /// <summary>
        /// 是否确定缓存肯定过期了
        /// </summary>
        private static bool CacheOutdated = false;
        public List<TCache> CacheSearchRange(Predicate<TCache> predicate)
        {
            List<TCache> res = [];
            EnsureCacheValidThen(() =>
            {
                foreach(var item in Cache)
                {
                    if(predicate(item))
                        res.Add(item);
                }
            });
            return res;
        }
        public TCache? CacheSearchSingle(Predicate<TCache> predicate)
        {
            TCache? res = default;
            EnsureCacheValidThen(() =>
            {
                res = Cache.Find(predicate);
            });
            return res;
        }
        protected int CacheCount()
        {
            int res = 0;
            EnsureCacheValidThen(() =>
            {
                res = Cache.Count;
            });
            return res;
        }
        protected void EnsureCacheValidThen(Action action)
        {
            lock (CacheAccessLock)
            {
                if (!CacheTrustable || CacheOutdated)
                {
                    if (CacheOutdated || ReadLastUpdate() > CacheLastUpdate)
                    {
                        Cache.Clear();
                        CacheLastUpdate = DateTime.Now;
                        Cache.AddRange(DbProjectToCache(Existing));
                    }
                    CacheTrustable = true;
                    CacheOutdated = false;
                }
                action();
            }
        }
        protected void CacheReset()
        {
            lock (CacheAccessLock)
            {
                Cache.Clear();
                CacheLastUpdate = DateTime.MinValue;
                CacheTrustable = false;
                CacheOutdated = false;
            }
        }
        protected override void PreWriteLastUpdate()
        {
            lock (CacheAccessLock)
            {
                CacheOutdated = true;
            }
        }
        protected abstract IQueryable<TCache> DbProjectToCache(IQueryable<T> source);
    }
}
